package cn.lg.soar.database.datascope;

import cn.lg.soar.common.util.current.CurrentThreadContext;
import cn.lg.soar.core.util.SoarSubscriber;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.time.Duration;
import java.util.*;

/**
 * @author luguoxiang 469599794@qq.com
 * @Date: 2024/3/1 21:59
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public final class DataScopeHolder {

    private static final ThreadLocal<String> RESOURCE_CODE = new ThreadLocal<>();
    private static final ThreadLocal<Integer> IGNORE = ThreadLocal.withInitial(() -> 0);

    private static final Cache<String, Long> CACHE = Caffeine.newBuilder()
            .initialCapacity(512)
            .maximumSize(4096)
            .expireAfterWrite(Duration.ofSeconds(30))
            .build();

    private static IDataPermissionService dataPermissionService;
    static {
        SoarSubscriber.subscribeInit(applicationContext -> {
            dataPermissionService = applicationContext.getBean(IDataPermissionService.class);
        });
    }

    /**
     * 设置资源编码
     * @param value
     */
    static void setResourceCode(String value) {
        RESOURCE_CODE.set(value);
    }

    /**
     * 删除资源编码
     */
    static void delResourceCode() {
        RESOURCE_CODE.remove();
    }

    /**
     * 添加忽略（从调用该方法的地方开始到 delIgnore 结束的所有数据库操作都将忽略数据权限控制）
     * 注意：必须和 delIgnore 成对使用，否则有内存泄漏风险
     */
    static void addIgnore() {
        IGNORE.set(IGNORE.get() + 1);
    }

    /**
     * 删除忽略
     */
    static void delIgnore() {
        int i = IGNORE.get() - 1;
        if (Objects.equals(i, 0)) {
            IGNORE.remove();
        } else {
            IGNORE.set(i);
        }
    }

    /**
     * 获取当前资源编码
     * @return
     */
    public static String getResourceCode() {
        String code = RESOURCE_CODE.get();
        if (code == null) {
            try {
                // 读取当前请求路径
                return CurrentThreadContext.getResource();
            } catch (RuntimeException e) {
                return null;
            }
        }
        return code;
    }

    /**
     * 获取资源id
     * @param mappedStatementId
     * @return
     */
    public static long getResourceId(String mappedStatementId) {
        String code = RESOURCE_CODE.get();
        if (code == null) {
            try {
                // 读取当前请求路径
                code = CurrentThreadContext.getResource();
            } catch (RuntimeException e) {
                return -1;
            }
            if (code == null) {
                return -1;
            }
        }
        return CACHE.get(code, c -> {
            // 判断是否忽略
            if (IGNORE_ID.contains(mappedStatementId)) {
                return -1L;
            }
            // 判断类是否忽略
            for (String s : IGNORE_CLASS) {
                if (mappedStatementId.startsWith(s)) {
                    IGNORE_ID.add(mappedStatementId);
                    return -1L;
                }
            }
            // 映射资源id
            Long resourceId = dataPermissionService.mapId(c);
            if (resourceId == null) {
                return -1L;
            }
            return resourceId;
        });
    }

    /**
     * 获取资源id
     * @param resourceCode 资源编码
     * @return
     */
    public static long getResourceIdByCode(String resourceCode) {
        return CACHE.get(resourceCode, c -> {
            Long resourceId = dataPermissionService.mapId(c);
            if (resourceId == null) {
                return -1L;
            }
            return resourceId;
        });
    }

    /**
     * 判断是否忽略
     * @return
     */
    public static boolean isIgnore() {
        return IGNORE.get() > 0;
    }

    private static final List<String> IGNORE_CLASS = new ArrayList<>();
    private static final Set<String> IGNORE_ID = new HashSet<>();

    /**
     * 添加忽略（忽略该类下所有方法的数据权限控制）
     * @param clazz 类
     */
    public static void addIgnore(Class<?> clazz) {
        IGNORE_CLASS.add(clazz.getName());
    }

    /**
     * 添加忽略（忽略某个mapper方法的数据权限控制）
     * @param mappedStatementId
     */
    public static void addIgnore(String mappedStatementId) {
        IGNORE_ID.add(mappedStatementId);
    }

}
